RDSのパスワードをSecrets Managerで暗号化するCloudFormationテンプレートを作成してみた

RDSのパスワードをSecrets Managerで暗号化するCloudFormationテンプレートを作成してみた

今回は、RDSのパスワードをSecrets Managerで管理する構成をCloudFormationテンプレートに記述してみたいと思います。 RDSのパスワードをテンプレートに直接記述することなく、安全に管理できる構成を目指します。
Clock Icon2023.10.04

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

こんにちは、おのやんです。

みなさん、RDSのパスワードは暗号化できていますか?私はできていませんでした(超絶懺悔)。

CloudFormationのテンプレートにて、RDSの設定を記述する場合があるかと思います。この際、CloudFormationテンプレートに直接パスワードなどの機密情報を記述するのは、セキュリティ上のベストプラクティスに反します。

テンプレートはソースコードリポジトリに保存されることも多く、手違いで公開されてしまうとパスワードが流出するリスクもあるため、平文記述は極力避けたいです。

ということで、今回はRDSのパスワードをSecrets Managerで管理する構成を、CloudFormationテンプレートに記述してみたいと思います。

危ない例

こちらは危険なCloudFormationテンプレートの例です。パスワードが平文で記述されています。このテンプレートがパブリックな場所に置かれると、パスワードが流出してしまいます。

Resources:
  RDSDBInstance:
    Type: "AWS::RDS::DBInstance"
    Properties:
      DBInstanceIdentifier: sample-rds
      DBName: db
      DBInstanceClass: !Ref DBInstanceClass
      Engine: mysql
      EngineVersion: 8.0
      Port: 3306
      PubliclyAccessible: false
      AllocatedStorage: "20"
      MasterUsername: admin
      MasterUserPassword: password # パスワードが平文で保存されている!これは危ない!
      DBSubnetGroupName: !Ref SubnetGroup

セキュアな例

こちらはセキュアなCloudFormationテンプレートの例です。RDSのパスワードをSecrets Managetで管理してあるため、セキュリティをより確保しながらRDSを構築することができます。

Resources:
  RDSSecret: # ここでRDSのパスワードとなるランダム文字列を生成
    Type: "AWS::SecretsManager::Secret"
    Properties:
      Name: "RDSSecret"
      Description: "RDS password for my RDS instance"
      GenerateSecretString:
        SecretStringTemplate: '{"username": "admin"}'
        GenerateStringKey: "password"
        PasswordLength: 16
        ExcludeCharacters: '"@/\'
  RDSDBInstance:
    Type: "AWS::RDS::DBInstance"
    Properties:
      DBInstanceIdentifier: sample-rds
      DBName: db
      DBInstanceClass: !Ref DBInstanceClass
      Engine: mysql
      EngineVersion: 8.0
      Port: 3306
      PubliclyAccessible: false
      AllocatedStorage: "20"
      MasterUsername: admin
      MasterUserPassword: !Sub '{{resolve:secretsmanager:${MyRDSSecret}:SecretString:password}}' # Secrets Managerのランダム文字列を間接的に参照!これなら安心!
      DBSubnetGroupName: !Ref SubnetGroup

CloudFormationテンプレートのSecret Manger部分の詳細

さきほどのCloudFormationテンプレートのSecret Manger部分を再度示します。

Resources:
  RDSSecret:
    Type: "AWS::SecretsManager::Secret"
    Properties:
      Name: "MyRDSSecret"
      Description: "RDS password for my RDS instance"
      GenerateSecretString:
        SecretStringTemplate: '{"username": "admin"}'
        GenerateStringKey: "password"
        PasswordLength: 16
        ExcludeCharacters: '"@/\'

SecretStringTemplateの部分では、シークレットに含める項目をJSON文字列として定義しています。ここに記載の通り、生成されるシークレットには"username":"admin"の項目が含まれることになります。

GenerateStringKeyの部分は、上記のSecretStringTemplateで定義したJSONに追加されるキーを示します。

PasswordLengthの部分では、ランダムに生成するパスワードの長さを指定します。ここでは16と指定していますので、16文字のランダムな文字列が生成されることになります。

ExcludeCharactersの部分では、パスワード生成時に含めない文字を指定します。特定のシステムで特定の文字列が使えない、みたいな場合に、それらの文字を除外します。今回では、"@/\の文字を除外しています。

これらの設定を踏まえると、16文字のランダムなパスワードが生成され、passwordというキーにランダムパスワードが対応付けられます。そして、JSONに{"username": "admin"}が追加されます。最終的なシークレットは、以下の通りになります。

{
  "username": "admin",
  "password": "<16文字のランダムなパスワード>"
}

CloudFormationテンプレートのRDS部分の詳細

さきほどのCloudFormationテンプレートのRDS部分を再度示します。

  RDSDBInstance:
    Type: "AWS::RDS::DBInstance"
    Properties:
      DBInstanceIdentifier: sample-rds
      DBName: db
      DBInstanceClass: !Ref DBInstanceClass
      Engine: mysql
      EngineVersion: 8.0
      Port: 3306
      PubliclyAccessible: false
      AllocatedStorage: "20"
      MasterUsername: admin
      MasterUserPassword: !Sub '{{resolve:secretsmanager:${RDSSecret}:SecretString:password}}'
      DBSubnetGroupName: !Ref SubnetGroup

こちらの項目にて、Secrets Managerに保存しているシークレットを参照しています。

MasterUserPassword: !Sub '{{resolve:secretsmanager:${RDSSecret}:SecretString:password}}'

基本的には、RDSSecretという名前でシークレットを指定しています。そして、このシークレットが持つpasswordという属性値をSecrets Managerから取得しています。取得したこの値は、RDSインスタンスのマスターユーザーのパスワードとして設定されます。

また、{{resolve:secretsmanager:${RDSSecret}:SecretString:password}}の部分では、Secrets Managerのシークレット値を解決しています。secretsmanagerの後に続くARNによって、特定のシークレットを指定しています。そして、SecretString:passwordの部分で、シークレットのJSON構造からパスワードを取得しているという感じです。

さいごに

RDSのパスワードをCloudFormationテンプレートで設定する際は、Secrets Managerを使うことで安全に管理できます。平文で保存することもないため、テンプレートからパスワードが漏れるといった心配もないですね。

本記事がお役に立てば幸いです。では!

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.